#include <gip.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

extern "C" {
#include <plane.h>
#include <scn.h>
#include <debug.h>
#include <render.h>
#include <transforms.h>
#include <visual_ppm.h>
#include <visual.h>
#include <raytrace.h>
#include <guistub.h>
}

//teste 

class App
{
public:
  int run( int argc, char *argv[] )
  {
    gip_app_main( argc, argv );
    return 0;
  }

  bool loadModel( const char *filename )
  {

    gui_init( (char*) filename );

    return true;
  }


  static void update( GipEvent *e )
  {
    App *a = singleton();
    a->update();
  }

  static void tour( GipEvent *e )
  {
    App *a = singleton();
    a->tour();
  }

  void tour( void ){

    light_t *light1 = light_new();

    LIGHT_X(light1) = 0;
    LIGHT_Y(light1) = 0;
    LIGHT_Z(light1) = 400;

    light_t *light2 = light_new();

    LIGHT_X(light2) = 0;
    LIGHT_Y(light2) = 0;
    LIGHT_Z(light2) = -400;

    int i,j;
    double rotx,roty;
    //while(1){
    for( j = 0 ; j < 4 ; j++ ){
      for( i = 1 ; i < 360 ; i+= 10 ){
	
	switch( j ){
	case 0:{
	  roty = ((double) 2*3.14 / (double) 360) * (double) i;
	  break;
	}
	case 1:{
	  rotx = ((double) 2*3.14 / (double) 360) * (double) i;
	  break;
	}
	case 2:{
	  roty = ((double) 2*3.14 / (double) 360) * (double) i;
	  rotx = ((double) 2*3.14 / (double) 360) * (double) i;
	  break;
	}
	default :{
	  roty = ((double) 2*3.14 / (double) 360) * (double) i;
	  rotx = ((double) 2*3.14 / (double) 360) * (double) i;
	  break;
	}
	}

	GipImage *img = doRender( rotx, roty, light1, light2 );

	gip_canvas_set_image( canvas, img );
	gip_image_destroy( img );
	
      }
    }




  }

  void update( void )
  {
    /* load values from GUI */
    double rot_x = getValue( entryRotHorz );
    double rot_y = getValue( entryRotVert );

    double e1_x  = getValue( entryExtra1X );
    double e1_y  = getValue( entryExtra1Y );
    double e1_z  = getValue( entryExtra1Z );
    
    double e2_x  = getValue( entryExtra2X );
    double e2_y  = getValue( entryExtra2Y );
    double e2_z  = getValue( entryExtra2Z );

    light_t *light1 = light_new();

    LIGHT_X(light1) = e1_x;
    LIGHT_Y(light1) = e1_y;
    LIGHT_Z(light1) = e1_z;

    light_t *light2 = light_new();

    LIGHT_X(light2) = e2_x;
    LIGHT_Y(light2) = e2_y;
    LIGHT_Z(light2) = e2_z;

    /* check values */
    clip( &rot_y, 0, 359 );
    clip( &rot_x, 0, 359 );
    
    GipImage *img = doRender( rot_x, rot_y, light1, light2 );

    gip_canvas_set_image( canvas, img );
    gip_image_destroy( img );

    return;


  }



  static App* singleton( char *title=0 )
  {
    if ( pinstance == 0 )
      {
	if ( title == 0 )
	  title = "<Unamed>";
	pinstance = new App( title );
      }
    return pinstance;
  }

protected:
  App( char *title )
  {
    gip_app_init( title );
    setupUi();
    model = 0;
  }


private:
  static App* pinstance;

  GipWidget *canvas;
  GipWidget *panelGroupWest;
  GipWidget *panelGroupEast;
  GipWidget *panelGroupSouth;

  GipWidget *panelCamera;
  GipWidget *labelRotHorz;
  GipWidget *labelRotVert;
  GipWidget *entryRotHorz;
  GipWidget *entryRotVert;

  GipWidget *panelPlanes;
  GipWidget *labelExtra1;
  GipWidget *labelExtra1X;
  GipWidget *labelExtra1Y;
  GipWidget *labelExtra1Z;
  GipWidget *labelExtra1RotHorz;
  GipWidget *labelExtra1RotVert;
  GipWidget *labelExtra2;
  GipWidget *labelExtra2X;
  GipWidget *labelExtra2Y;
  GipWidget *labelExtra2Z;
  GipWidget *labelExtra2RotHorz;
  GipWidget *labelExtra2RotVert;
  GipWidget *labelFront;
  GipWidget *labelBack;
  GipWidget *labelLeft;
  GipWidget *labelRight;
  GipWidget *labelTop;
  GipWidget *labelBottom;
  GipWidget *labelVMin;
  GipWidget *labelVMax;
  GipWidget *entryExtra1;
  GipWidget *entryExtra1X;
  GipWidget *entryExtra1Y;
  GipWidget *entryExtra1Z;
  GipWidget *entryExtra1RotHorz;
  GipWidget *entryExtra1RotVert;
  GipWidget *entryExtra2;
  GipWidget *entryExtra2X;
  GipWidget *entryExtra2Y;
  GipWidget *entryExtra2Z;
  GipWidget *entryExtra2RotHorz;
  GipWidget *entryExtra2RotVert;
  GipWidget *entryFront;
  GipWidget *entryBack;
  GipWidget *entryLeft;
  GipWidget *entryRight;
  GipWidget *entryTop;
  GipWidget *entryBottom;
  GipWidget *entryVMin;
  GipWidget *entryVMax;

  GipWidget *panelButtons;
  GipWidget *buttonUpdate;
  GipWidget *buttonTour;

  double minX, maxX;
  double minY, maxY;
  double minZ, maxZ;

  model3d_t *model;


  void setupUi( void )
  {
    canvas = gip_canvas_new();

    setupPanels();
    setupConnections();

    setWidgetsVisibile( false );

  }


  void setupPanels( void )
  {
    panelGroupWest = gip_panel_group_new();
    panelGroupEast = gip_panel_group_new();
    panelGroupSouth = gip_panel_group_new();

    gip_app_add_panel_group( panelGroupWest, GIP_WEST );
    gip_app_add_panel_group( panelGroupEast, GIP_EAST );
    gip_app_add_panel_group( panelGroupSouth, GIP_SOUTH );

    setupPanelCamera();
    setupPanelButtons();
  }

  void setupPanelCamera( void )
  {
    panelCamera = gip_panel_new( GIP_VERT, 0, 0 );
    gip_panel_group_add( panelGroupWest, panelCamera );

    gip_panel_add( panelCamera, gip_label_new( "Camera Rotation:" ) );

    labelRotHorz = gip_label_new( "Horizontal (0-359)" );
    labelRotVert = gip_label_new( "Vertical (0-359)" );

    entryRotHorz = gip_text_entry_new( "0" );
    entryRotVert = gip_text_entry_new( "0" );

    gip_panel_add( panelCamera, labelRotHorz );
    gip_panel_add( panelCamera, entryRotHorz );
    gip_panel_add( panelCamera, labelRotVert );
    gip_panel_add( panelCamera, entryRotVert );

    gip_panel_add( panelCamera, gip_label_new( "First Extra Light:" ) );

    labelExtra1X = gip_label_new( "X" );
    labelExtra1Y = gip_label_new( "Y" );
    labelExtra1Z = gip_label_new( "Z" );

    entryExtra1X = gip_text_entry_new( "0" );
    entryExtra1Y = gip_text_entry_new( "0" );
    entryExtra1Z = gip_text_entry_new( "400" );

    gip_panel_add( panelCamera, labelExtra1X );
    gip_panel_add( panelCamera, entryExtra1X );
    gip_panel_add( panelCamera, labelExtra1Y );
    gip_panel_add( panelCamera, entryExtra1Y );
    gip_panel_add( panelCamera, labelExtra1Z );
    gip_panel_add( panelCamera, entryExtra1Z );

    gip_panel_add( panelCamera, gip_label_new( "Second Extra Light:" ) );

    labelExtra2X = gip_label_new( "X" );
    labelExtra2Y = gip_label_new( "Y" );
    labelExtra2Z = gip_label_new( "Z" );

    entryExtra2X = gip_text_entry_new( "0" );
    entryExtra2Y = gip_text_entry_new( "100" );
    entryExtra2Z = gip_text_entry_new( "500" );

    gip_panel_add( panelCamera, entryExtra2X );
    gip_panel_add( panelCamera, labelExtra2Y );
    gip_panel_add( panelCamera, entryExtra2Y );
    gip_panel_add( panelCamera, labelExtra2Z );
    gip_panel_add( panelCamera, entryExtra2Z );

  }


  void setupPanelButtons( void )
  {
    panelButtons = gip_panel_new( GIP_HORZ, 0, 0 );
    gip_panel_group_add( panelGroupSouth, panelButtons );

    buttonUpdate = gip_button_new( "Update" );
    gip_panel_add( panelButtons, buttonUpdate );

    //buttonTour = gip_button_new( "Tour" );
    //gip_panel_add( panelButtons, buttonTour );
  }


  void setupConnections( void )
  {
    gip_widget_set_callback( buttonUpdate, update );
    //gip_widget_set_callback( buttonTour,   tour );
  }


  double getValue( GipWidget *w )
  {
    return atof( gip_text_entry_get( w ) );
  }

  void setValue( GipWidget *w, double v )
  {
    char b[ 64 ];
    snprintf( b, sizeof(b), "%0.3lf", v );
    gip_text_entry_set( w, b );
  }

  void setValue( GipWidget *w, uint16_t v )
  {
    char b[ 64 ];
    snprintf( b, sizeof(b), "%u", v );
    gip_text_entry_set( w, b );
  }


  void setWidgetsVisibile( bool visible )
  {
    /* not implemented in libgip :-( */
    //gip_widget_set_visible( panelPlanes, visible );
  }



  GipImage *doRender( double rotx, double roty, light_t *l1, light_t *l2 )
  {

    surface_t * surface = gui_display( deg2rad( rotx ), deg2rad( roty ), l1, l2 );

    GipImage *img = gip_image_new( surface->size.x, surface->size.y );
    int i, j;

    for ( i=0; i < surface->size.x; i++ )
      for ( j=0; j < surface->size.y; j++ )
	{
	  color_t c = PIXEL_AT( surface, i, j );
	  gip_image_draw_point( img, i, j, c.r, c.g, c.b );
	}

    surface_free( surface );

    return img;
  }

  static void clip( double *p, const double min, const double max )
  {
    if ( *p < min )
      *p = min;
    else if ( *p > max )
      *p = max;
  }

  static void clip( double *a, double *b, const double min, const double max )
  {
    if ( *a >= *b )
      {
	if ( ( *b - 1 ) >= min )
	  *a = *b - 1;
	else
	  *b = *a + 1;
      }

    clip( a, min, max );
    clip( b, min, max );
  }

  static double deg2rad( double radians )
  {
    return M_PI * radians / 180.0;
  }

};

App* App::pinstance = 0;


int main( int argc, char **argv )
{
  if ( argc < 2 )
    {
      printf( "Usage:\n"
	      "\t%s <file.scn>\n"
	      "\n",
	      argv[ 0 ] );
      return -1;
    }

  App *a = App::singleton( "[MC930] Project-03: Gustavo Barbieri, "
			   "Samuel Goto" );

  a->loadModel( argv[ 1 ] );

  a->update();

  return a->run( argc, argv );
}
